/* =================================================
 * Package:			dk.iha.toksEditor
 * File:			DBAdapter.java
 * Description:		This class is responsible for
 * 					the local TOKS database.
 * 
 * Responsible
 * =================================================
 * Name:			Jakob Sulby
 * Study number:	09136
 */

package dk.iha.toksEditor;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

public class DBAdapter extends ContentProvider
{
	private static final String DATABASE_NAME = "toksEditor4.db";
	private static final String DATABASE_TABLE = "Patients";
	private static final int DATABASE_VERSION = 4;
	private SQLiteDatabase db;
	private final Context context;
	private DBOpenHelper dbHelper;
	
	//Column names
	public static final String KEY_ID = "_id";
	public static final String KEY_PATIENT_NAME = "patientName";
	public static final String KEY_PATIENT_CPR = "patientCPR";
	public static final String KEY_PATIENT_NOTE = "patientNote";
	public static final String KEY_PATIENT_UPDATED_DATE= "patientUpdatedDate";
	public static final String KEY_TOKS_RESPIRATION = "toksRespiration";
	public static final String KEY_TOKS_SYSTOLIC = "toksSystolic";
	public static final String KEY_TOKS_HEART_RATE = "toksHartRate";
	public static final String KEY_TOKS_TEMPERATURE = "toksTemperature";
	public static final String KEY_TOKS_SATURATION = "toksSaturation";
	public static final String KEY_TOKS_DIASTOLIC = "toksDiastolic";
	public static final String KEY_TOKS_CONSCIOUSNESS = "toksConsciousness";
	public static final String KEY_TOKS_OXYGEN = "toksOxygen";
	public static final String KEY_TOKS_TOTAL_SCORE = "toksTotalScore";
	
	public DBAdapter() 
	{
		this.context = getContext();
	}
	
	public DBAdapter(Context _context) 
	{
		this.context = _context;
		dbHelper = new DBOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
	}
	
	/**
	 * Closes the connection to the database.
	 */
	public void close() 
	{
		db.close();
	}
	
	/**
	 * Open the connection to the database.
	 * @throws SQLiteException If it can't open a writable database connection it will try to open a readable connection.
	 */
	public void open() throws SQLiteException 
	{
		try 
		{
			db = dbHelper.getWritableDatabase();
		} 
		
		catch (SQLiteException ex) 
		{
			db = dbHelper.getReadableDatabase();
		}
	}
	
	/**
	 * Open the connection to the database in read only mode.
	 * @throws SQLiteException If it can't open a database connection.
	 */
	public void openReadOnly() throws SQLiteException 
	{
		try 
		{
			db = dbHelper.getReadableDatabase();
		} 
		
		catch (SQLiteException ex) 
		{
			throw new SQLException("Can't open the database.");
		}
	}
	
	/**
	 * Insert a new patient in the database
	 * @param The patient to insert.
	 * @return The row ID of the newly inserted row, or -1 if an error occurred. 
	 */
	public long insertPatient(Patient _patient) 
	{
		// Create a new row of values to insert.
		ContentValues newValues = new ContentValues();
		
		// Assign values for each row.
		newValues.put(KEY_PATIENT_NAME, _patient.getName());
		newValues.put(KEY_PATIENT_CPR, _patient.getCPR());
		newValues.put(KEY_PATIENT_NOTE, _patient.getNote());
		newValues.put(KEY_PATIENT_UPDATED_DATE, _patient.getLastUpdated().getTime());
		newValues.put(KEY_TOKS_CONSCIOUSNESS, _patient.getTOKS().getConsciousness());
		newValues.put(KEY_TOKS_DIASTOLIC, _patient.getTOKS().getBloodPressureDiastolic());
		newValues.put(KEY_TOKS_HEART_RATE, _patient.getTOKS().getHeartRate());
		newValues.put(KEY_TOKS_OXYGEN, _patient.getTOKS().getOxygenLitersPerMinute());
		newValues.put(KEY_TOKS_RESPIRATION, _patient.getTOKS().getRespirationFrequency());
		newValues.put(KEY_TOKS_SATURATION, _patient.getTOKS().getSaturation());
		newValues.put(KEY_TOKS_SYSTOLIC, _patient.getTOKS().getBloodPressureSystolic());
		newValues.put(KEY_TOKS_TEMPERATURE, _patient.getTOKS().getTemperature());
		newValues.put(KEY_TOKS_TOTAL_SCORE, _patient.getTOKS().getTotalScore());
		
		// Insert the row.
		return db.insert(DATABASE_TABLE, null, newValues);
	}
	
	/**
	 * Remove a patient based on its cpr number.
	 * @param The patient to remove.
	 * @return Returns if it succeeded.
	 */
	public boolean removePatient(Patient _patient) 
	{
		try 
		{
			Cursor c = getPatient(_patient.getCPR());
			long rowIndex = Long.parseLong(c.getString(0));
			
			return db.delete(DATABASE_TABLE, KEY_ID + "=" + rowIndex, null) > 0;
		} 
		
		catch (Exception e) {
			return false;
		}
	}
	
	/**
	 * Update a patients saved data. If the patient does not exist, it will try to insert the patient (as a new one).
	 * @param The patient to update.
	 * @return If it went well
	 */
	public boolean updatePatient(Patient _patient) 
	{		
		try 
		{
			Cursor c = getPatient(_patient.getCPR());
			String test = c.getString(0);
			long rowIndex = Long.parseLong(test);
			
			ContentValues newValue = new ContentValues();
			
			newValue.put(KEY_PATIENT_NAME, _patient.getName());
			newValue.put(KEY_PATIENT_CPR, _patient.getCPR());
			newValue.put(KEY_PATIENT_NOTE, _patient.getNote());
			newValue.put(KEY_PATIENT_UPDATED_DATE, _patient.getLastUpdated().getTime());
			newValue.put(KEY_TOKS_CONSCIOUSNESS, _patient.getTOKS().getConsciousness());
			newValue.put(KEY_TOKS_DIASTOLIC, _patient.getTOKS().getBloodPressureDiastolic());
			newValue.put(KEY_TOKS_HEART_RATE, _patient.getTOKS().getHeartRate());
			newValue.put(KEY_TOKS_OXYGEN, _patient.getTOKS().getOxygenLitersPerMinute());
			newValue.put(KEY_TOKS_RESPIRATION, _patient.getTOKS().getRespirationFrequency());
			newValue.put(KEY_TOKS_SATURATION, _patient.getTOKS().getSaturation());
			newValue.put(KEY_TOKS_SYSTOLIC, _patient.getTOKS().getBloodPressureSystolic());
			newValue.put(KEY_TOKS_TEMPERATURE, _patient.getTOKS().getTemperature());
			newValue.put(KEY_TOKS_TOTAL_SCORE, _patient.getTOKS().getTotalScore());
			
			return db.update(DATABASE_TABLE, newValue, KEY_ID + "=" + rowIndex, null) > 0;
		}
		
		catch (Exception e) 
		{
			if(insertPatient(_patient) != -1)
				return true;
			else 
				return false; 
		}
	}
	
	/**
	 * Find all patients in the database
	 * @return returns a cursor to the objects
	 */
	public Cursor getAllItemsCursor() 
	{
		return db.query(DATABASE_TABLE,
				new String[] {KEY_ID, 
								KEY_PATIENT_NAME, 
								KEY_PATIENT_CPR, 
								KEY_PATIENT_NOTE, 
								KEY_PATIENT_UPDATED_DATE, 
								KEY_TOKS_RESPIRATION, 
								KEY_TOKS_SYSTOLIC, 
								KEY_TOKS_HEART_RATE, 
								KEY_TOKS_TEMPERATURE, 
								KEY_TOKS_SATURATION, 
								KEY_TOKS_DIASTOLIC, 
								KEY_TOKS_CONSCIOUSNESS, 
								KEY_TOKS_OXYGEN, 
								KEY_TOKS_TOTAL_SCORE},
		null, null, null, null, null);
	}
	
	/**
	 * Get a specific patient
	 * @param The cpr number of the patient to find.
	 * @return returns a cursor to the patient in the database
	 * @throws SQLException throws if nothing found on the specified row index
	 */
	public Cursor getPatient(String _cpr) throws SQLException 
	{
		Cursor result = db.query(true, DATABASE_TABLE, new String[] {KEY_ID, 
				KEY_PATIENT_NAME, 
				KEY_PATIENT_CPR, 
				KEY_PATIENT_NOTE, 
				KEY_PATIENT_UPDATED_DATE, 
				KEY_TOKS_RESPIRATION, 
				KEY_TOKS_SYSTOLIC, 
				KEY_TOKS_HEART_RATE, 
				KEY_TOKS_TEMPERATURE, 
				KEY_TOKS_SATURATION, 
				KEY_TOKS_DIASTOLIC, 
				KEY_TOKS_CONSCIOUSNESS, 
				KEY_TOKS_OXYGEN, 
				KEY_TOKS_TOTAL_SCORE},
				KEY_PATIENT_CPR + "=" + _cpr, null, null, null, null, null);
		
		if ((result.getCount() == 0) || !result.moveToFirst()) 
		{
			throw new SQLException("No patient found with cpr: " + _cpr);
		}
		
		return result;
	}
	
	/**
	 * Get the number of patients in the database.
	 * @return Returns the number of patients in the database.
	 */
	public Integer getPatientCount()
	{
		Cursor result = db.query(true, DATABASE_TABLE, new String[] {KEY_ID, KEY_PATIENT_NAME},
				null, null, null, null, null, null);
		
		return result.getCount();
	}
	
	private static class DBOpenHelper extends SQLiteOpenHelper {
		public DBOpenHelper(Context context, String name, CursorFactory factory, int version) 
		{
			super(context, name, factory, version);
		}
		
		// SQL Statement to create a new database.
		private static final String DATABASE_CREATE = "create table " +
			DATABASE_TABLE + " (" + 
			KEY_ID + " integer primary key autoincrement, " +
			KEY_PATIENT_NAME + " text not null, " + 
			KEY_PATIENT_CPR + " text not null, " +
			KEY_PATIENT_NOTE + " text, " +
			KEY_PATIENT_UPDATED_DATE + " text, " +
			KEY_TOKS_RESPIRATION + " Integer, " +
			KEY_TOKS_SYSTOLIC + " Integer, " +
			KEY_TOKS_HEART_RATE + " Integer, " +
			KEY_TOKS_TEMPERATURE + " Double, " +
			KEY_TOKS_SATURATION + " Integer, " +
			KEY_TOKS_DIASTOLIC + " Integer, " +
			KEY_TOKS_CONSCIOUSNESS + " text, " +
			KEY_TOKS_OXYGEN + " Double, " +
			KEY_TOKS_TOTAL_SCORE + " Integer);";		
		
		@Override
		public void onCreate(SQLiteDatabase _db) 
		{
			_db.execSQL(DATABASE_CREATE);
		}
		
		@Override
		public void onUpgrade(SQLiteDatabase _db, int _oldVersion, int _newVersion) 
		{
			Log.w("TaskDBAdapter", "Upgrading from version " +
			_oldVersion + " to " +
			_newVersion + ", which will destroy all old data");
			
			// Drop the old table.
			_db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE);
			
			// Create a new one.
			onCreate(_db);
		}
	}
	
	//The content provider part of the database
	private static final String myURI = "content://dk.iha.toksEditor.toks-editor/unfinishedObservations";
	public static final Uri CONTENT_URI = Uri.parse(myURI);
	private static final int ALLROWS = 1;
	private static final int SINGLE_ROW = 2;
	private static final UriMatcher uriMatcher;
	
	static 
	{
		uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		uriMatcher.addURI("dk.iha.toksEditor.toks-editor", "unfinishedObservations", ALLROWS);
		uriMatcher.addURI("dk.iha.toksEditor.toks-editor", "unfinishedObservations/#", SINGLE_ROW);
	}
	
	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		int count;
		
		switch (uriMatcher.match(uri)) 
		{
			case ALLROWS:
				count = db.delete(DATABASE_TABLE, KEY_ID + "=", selectionArgs);
			break;
			
			case SINGLE_ROW:
				String segment = uri.getPathSegments().get(1);
				count = db.delete(DATABASE_TABLE, KEY_ID + "="
				+ segment
				+ (!TextUtils.isEmpty(KEY_ID) ? " AND ("
				+ KEY_ID + ")" : ""), selectionArgs);
			break;
			
			default: 
				throw new IllegalArgumentException("Unsupported URI: " + uri);
		}
		
		getContext().getContentResolver().notifyChange(uri, null);
		
		return count;
	}

	@Override
	public String getType(Uri uri) 
	{
		switch (uriMatcher.match(uri)) 
		{
			case ALLROWS: 
				return "vnd.android.cursor.dir/vnd.dk.iha.toks-ditor.unfinishedObservations";  //muligvis uden .dk.
				
			case SINGLE_ROW: 
				return "vnd.android.cursor.item/vnd.dk.iha.toks-ditor.unfinishedObservations";
				
			default: 
				throw new IllegalArgumentException("Unsupported URI: " + uri);
		}
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		// Insert the new row, will return the row number if successful.
		long rowID = db.insert(DATABASE_TABLE, "observation", new ContentValues());
		
		// Return a URI to the newly inserted row on success.
		if (rowID > 0) 
		{
			Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
			getContext().getContentResolver().notifyChange(uri, null);
			return uri;
		}
		
		throw new SQLException("Failed to insert row into " + uri);
	}

	@Override
	public boolean onCreate() {
		Context context = getContext();
		
		DBOpenHelper databaseHelper = new DBOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
		db = databaseHelper.getWritableDatabase();
		
		return (db == null) ? false : true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 
	{
		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
		qb.setTables(DATABASE_TABLE);
		
		// If this is a row query, limit the result set to the passed in row.
		switch (uriMatcher.match(uri)) 
		{
			case SINGLE_ROW: 
				qb.appendWhere(KEY_ID + "=" + uri.getPathSegments().get(1));
				break;
			
			default : break;
		}
		
		// If no sort order is specified sort by date / time
		String orderBy = KEY_PATIENT_NAME;
		
		// Apply the query to the underlying database.
		Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
		
		// Register the contexts ContentResolver to be notified if
		// the cursor result set changes.
		c.setNotificationUri(getContext().getContentResolver(), uri);
		
		// Return a cursor to the query result.
		return c;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) 
	{
		int count;
		
		switch (uriMatcher.match(uri)) 
		{
			case ALLROWS: 
				count = db.update(DATABASE_TABLE, values,
				KEY_ID, selectionArgs);
				break;
				
			case SINGLE_ROW: String segment = uri.getPathSegments().get(1);
				count = db.update(DATABASE_TABLE, values, KEY_ID
				+ "=" + segment
				+ (!TextUtils.isEmpty(KEY_ID) ? " AND ("
				+ KEY_ID + ")" : ""), selectionArgs);
				break;
				
			default: 
				throw new IllegalArgumentException("Unknown URI " + uri);
		}
		
		getContext().getContentResolver().notifyChange(uri, null);
		
		return count;
	}
}
